package istar.agent;

import istar.IActor;
import istar.IAgent;
import istar.IBasicElement;
import istar.IDependency;
import istar.IElement;
import istar.IElementLink;
import istar.IPosition;
import istar.IResource;
import istar.IRole;
import istar.IistarBehaviour;
import istar.behaviour.AbstractIstarBehaviour;
import istar.behaviour.IstarParallelBehaviour;
import istar.behaviour.MeansEndUniqueBehaviour;
import istar.behaviour.SequentialTaskBehaviour;
import istar.impl.AbstractMainGoal;
import istar.impl.Actor;
import istar.impl.Role;
import istar.impl.Belief;
import istar.impl.IstarAgent;
import istar.impl.MeansEndLink;
import istar.impl.Task;
import istar.impl.TaskDecompositionLink;
import istar.onto.IstarOntology;
import jade.core.AID;
import jade.core.Agent;
import jade.core.ServiceException;
import jade.core.behaviours.Behaviour;
import jade.core.behaviours.FSMBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.ParallelBehaviour;
import jade.core.messaging.TopicManagementHelper;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPANames;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.Property;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import javax.lang.model.element.Element;

import log.Log;


public class IstarJadeAgent extends Agent implements IAgent {

	// states
	private static final String INITIAL = "Initial";
	private static final String ACTIVE = "Active";
	private static final String FINAL = "Final";
	private static final String SUSPENDED = "Suspended";
	//private static final String ACTIVE_DELIBERATED = "Active_deliberated";
	private static final String ACTIVE_UNDEFINED = "Active_undefined";
	private static final String ACTIVE_SUCCEEDED = "Active_succeeded";
	private static final String ACTIVE_FAILED = "Active_failed";
	private static final String FINAL_STATE = "Final_State";
	
	// actions
	private static final int ADOPT = 0;
	private static final int ACTIVATE = 1;
	private static final int SUSPEND = 2;
	private static final int FAIL = 3;
	private static final int SUCCEED = 4;
	private static final int RETRY = 5;
	private static final int REACTIVATE = 6;
	private static final int DROPFAILURE = 7;
	private static final int DROPSUCCESS = 8;
	private static final int ACHIEVE = 9;
	

	private boolean log = true;
	private String nickname, type, id;
	private List<IElement> dependeeElements = new Vector<IElement>();
	private List<IElement> dependerElements = new Vector<IElement>();
	private List<IElement> inboundaryElements = new Vector<IElement>();
	private List<IPosition> occupies = new Vector<IPosition>();
	private List<IRole> plays = new Vector<IRole>();
	private List<Belief> beliefs = new Vector<Belief>();
	private List<IResource> resources = new Vector<IResource>();
	private HashMap<String, HashMap<String, Object>> knowledgeSet = new HashMap<String, HashMap<String, Object>>();
	private HashMap<String, Object> shortTermMemory = new HashMap<String, Object>();
	private AID sender = null;
	private HashMap<String, ACLMessage> workingMessages = new HashMap<String, ACLMessage>();
	private HashMap<String, ACLMessage> waitingMessages = new HashMap<String, ACLMessage>();

	private HashMap<String, HashMap<String, Object>> parameters = new HashMap<String, HashMap<String, Object>>();
	private HashMap<String, HashMap<String, Object>> result = new HashMap<String, HashMap<String, Object>>();

	
	private int ticketNumber = 0;
	
	public String configurationPath = "";
	private String logPath=null;
	
	public IstarJadeAgent(String _nickname){
		super();
		this.nickname= _nickname; 
	}
	
	public IstarJadeAgent() {
		super();
	}


	public Object getKnowledgeElement(String execNumber, String elemntID){
		HashMap<String, Object> elements = (HashMap<String, Object>)this.knowledgeSet.get(execNumber);
		return elements.get(elemntID);
	}
	
	
	public void setKnowledgeElement(String execNumber, IElement element){
		if (this.knowledgeSet.containsKey(execNumber)){
			HashMap<String, Object> elements = this.knowledgeSet.get(execNumber);
			if (elements.containsKey(element.getId()))
				elements.remove(element.getId());
			elements.put(element.getId(), element);
			if (element.getClass().equals(Belief.class)){
				beliefs.add((Belief)element);
			}
		}
		else
		{
			HashMap<String, Object> elements = new HashMap<String, Object>();
			elements.put(element.getId(),element);
			this.knowledgeSet.put(execNumber, elements);
		}
	}
	

	public void setKnowledgeObject(String execNumber, String objName, Object obj){
		if (this.knowledgeSet.containsKey(execNumber)){
			HashMap<String, Object> elements = this.knowledgeSet.get(execNumber);
			
			if (elements.containsKey(objName))
				elements.remove(objName);
			elements.put(objName, obj);
		}
		else
		{
			HashMap<String, Object> elements = new HashMap<String, Object>();
			elements.put(objName,obj);
			this.knowledgeSet.put(execNumber, elements);
		}
	}
	
	public void setParameter(String execNumber, String objName, Object obj){
		if (this.parameters.containsKey(execNumber)){
			HashMap<String, Object> params = this.parameters.get(execNumber);
			
			if (params.containsKey(objName))
				params.remove(objName);
			params.put(objName, obj);
		}
		else
		{
			HashMap<String, Object> params = new HashMap<String, Object>();
			params.put(objName,obj);
			this.parameters.put(execNumber, params);
		}
	}
	
	public Object getParameter(String execNumber, String elemntName){
		HashMap<String, Object> params = (HashMap<String, Object>)this.parameters.get(execNumber);
		if (params!=null)
			return params.get(elemntName);
		else
			return null;
	}

	public void setResult(String execNumber, String objName, Object obj){
		if (this.result.containsKey(execNumber)){
			HashMap<String, Object> rslts = this.result.get(execNumber);
			
			if (rslts.containsKey(objName))
				rslts.remove(objName);
			rslts.put(objName, obj);
		}
		else
		{
			HashMap<String, Object> rslts = new HashMap<String, Object>();
			rslts.put(objName,obj);
			this.result.put(execNumber, rslts);
		}
	}
	
	public Object getResult(String execNumber, String elemntName){
		HashMap<String, Object> rslts = (HashMap<String, Object>)this.result.get(execNumber);
		if (rslts!=null)
			return rslts.get(elemntName);
		else
			return null;
	}
	
	public String getNextTicket(){
		ticketNumber++;
		String ticket = this.getName()+"_" + ticketNumber; 
		HashMap<String, Object> elements = new HashMap<String, Object>();
		this.knowledgeSet.put(ticket, elements);
		return ticket;
	}

	public String getTicket(){
		return this.getName()+"_" + ticketNumber;
	}
	
	public HashMap<String, HashMap<String, Object>> getBeliefSet() {
		return knowledgeSet;
	}



	public boolean getLog() {
		return log;
	}

	public void setLog(boolean log) {
		this.log = log;
	}

	// Put agent initializations here
	protected void setup() {
		// Printout a welcome message
		//this.log("state=" + this.getState());
		
		Object[] args = getArguments();

		if (args != null && args.length > 0) {
			
			IActor actor = (IActor)args[0];
			//logPath = (String)args[1];
			configurationPath = (String)args[1];

			try {
				Iterator<IElement> itMainElmnts = actor.getMainElements().iterator();
				while (itMainElmnts.hasNext()){
					IElement main = (IElement)itMainElmnts.next();
					IElement clone = (IElement)main.clone(this);
				}
			
				Iterator<IElement> itDependees = actor.getDependeeElements().iterator();
				while (itDependees.hasNext()){
					IElement dependeeElmnt = itDependees.next();
					this.dependeeElements.add(dependeeElmnt);
				}
	
				Iterator<IElement> itDependers = actor.getDependerElements().iterator();
				while (itDependers.hasNext()){
					IElement dependerElmnt = itDependers.next();
					this.dependerElements.add(dependerElmnt);
				}
			
			
				if (actor.getClass().equals(IstarAgent.class)){
					Iterator<IPosition> itPositions = ((IstarAgent)actor).getOccupies().iterator();
					while (itPositions.hasNext()){
						this.addPosition(itPositions.next());
					}
					Iterator<IRole> itRoles = ((IstarAgent)actor).getPlays().iterator();
					while (itRoles.hasNext()){
						this.addRole(itRoles.next());
					}
				}
			
				testClone();

				// registrando servicos
				List<ServiceDescription> listSD = actorServiceDescription(actor);
				addServices(listSD);

				/*
				if (this.getDependeeElements().size()>0){
					List<ServiceDescription> listSD = actorServiceDescription(actor);
					//registerServices(listSD);
					addServices(listSD);
				}
				*/
			

			
				List<IElement> mainElements = new Vector<IElement>();
				mainElements.addAll(this.getMainElements());
				
				Iterator<IRole> itRoles = this.plays.iterator();
				while (itRoles.hasNext()){
					IRole role = itRoles.next();
					mainElements.addAll(role.getMainElements());
				}

				Iterator<IPosition> itPositions = this.getOccupies().iterator();
				while (itPositions.hasNext()){
					IPosition position = itPositions.next();
					itRoles = position.getCovers().iterator();
					while (itRoles.hasNext()){
						IRole role = itRoles.next();
						mainElements.addAll(role.getMainElements());
					}
				}

				
				/*
				Iterator<IElement> itMains = mainElements.iterator();
				while (itMains.hasNext()){
					setMainBehaviour(itMains.next());
				}
				*/
				setMainBehaviour(mainElements);
				
			} catch (Exception e) {
				System.out.println(this.getName()+ " erro-> "+  e.getMessage());
			}
		}
		
		else {
			System.err.println("No istaragent specified");
		}
	}
	
	
	
	private void setMainBehaviour(IElement mainElement) throws Exception{
		String initial_state = INITIAL; //behaviour.getBehaviourName();
		String active_state = ACTIVE;//subBehaviour.getBehaviourName();
		String reset_state = "RESET_BEHAVIOUR";
		String error_state = "ERROR_STATE";
		
		if (mainElement!=null){
			Behaviour behaviour = null;			
			Behaviour subBehaviour = null;
			try {
				AbstractMainGoal mainGoal = (AbstractMainGoal)mainElement;
				behaviour = mainGoal.getMainBehaviour();
				if (mainElement.getMeansEndLink().getType()==IElementLink.PARALLEL){
					subBehaviour = new IstarParallelBehaviour(this, mainElement, ParallelBehaviour.WHEN_ANY);
				}
				else if (mainElement.getMeansEndLink().getType()==IElementLink.UNIQUE){
					subBehaviour = new MeansEndUniqueBehaviour(this, (IElement)mainElement);
				}
			} catch (ClassCastException ce) {
				ce.printStackTrace();
				if (mainElement.isBasic()){
					behaviour = ((IBasicElement)mainElement).getBehaviour();
				}
				else {
					if (mainElement.getClass()==Task.class){
						if (((Task)mainElement).getMyDecompositionLink().getType()==IElementLink.SEQUENTIAL){
							behaviour = new SequentialTaskBehaviour(this, (Task)mainElement);
						}
						else if (((Task)mainElement).getMyDecompositionLink().getType()==IElementLink.PARALLEL){
							behaviour = new IstarParallelBehaviour(this, mainElement, ParallelBehaviour.WHEN_ALL);
						}
					} 
					else {
						if (mainElement.getMeansEndLink().getType()==IElementLink.PARALLEL){
							behaviour = new IstarParallelBehaviour(this, mainElement, ParallelBehaviour.WHEN_ANY);
						}
						else if (mainElement.getMeansEndLink().getType()==IElementLink.UNIQUE){
							behaviour = new MeansEndUniqueBehaviour(this, mainElement);
						}
					}
				}
			}
			if (behaviour!=null){

				FSMBehaviour fsm = new FSMBehaviour(this) {
					public int onEnd() {
						myAgent.doDelete();
						return super.onEnd();
					}
				};
				
				fsm.registerTransition(initial_state, initial_state, 0);
				fsm.registerTransition(initial_state, active_state, 1);
				fsm.registerTransition(initial_state, FINAL, 2);
				fsm.registerTransition(active_state, active_state, 0);
				fsm.registerTransition(active_state, reset_state, 1);
				fsm.registerDefaultTransition(reset_state, initial_state);

				fsm.registerTransition(active_state, error_state, AbstractIstarBehaviour.ERROR_STATE_CODE);
				fsm.registerDefaultTransition(error_state, reset_state);
				
				fsm.registerState(new ResetBehaviour(fsm), reset_state);

				fsm.registerFirstState(behaviour, initial_state);
				fsm.registerState(subBehaviour, active_state);
				fsm.registerLastState(new EndBehaviour(), FINAL);

				fsm.registerState(new ErrorBehaviour(fsm), error_state);

				log("behaviour type=" + behaviour.getClass()+ " name=" + behaviour.getBehaviourName()+ " state=" +  behaviour.getExecutionState());
				log("---------------------");
				log("FSM :" + fsm.getChildren().toString() );
				log("---------------------");
				this.addBehaviour(fsm);
			}
			else {
				throw new Exception("behaviour is null!");
			}
		
		}
		
	}
	
	private void setMainBehaviour(List<IElement> mainElements) throws Exception{

		String initial_state = INITIAL; //behaviour.getBehaviourName();
		String active_state = ACTIVE;//subBehaviour.getBehaviourName();
		String reset_state = "RESET_BEHAVIOUR";
		String error_state = "ERROR_STATE";
		
		if (mainElements!=null){
			if (!mainElements.isEmpty()){
				Behaviour behaviour = null;			
				Behaviour subBehaviour = null;
				ParallelBehaviour mainBehaviour = new ParallelBehaviour(this, ParallelBehaviour.WHEN_ANY);
				Iterator<IElement> itMains = mainElements.iterator();
				while (itMains.hasNext()){
					IElement mainElement = itMains.next();
					try {
						AbstractMainGoal mainGoal = (AbstractMainGoal)mainElement;
						behaviour = mainGoal.getMainBehaviour();
						if (mainElement.getMeansEndLink().getType()==IElementLink.PARALLEL){
							subBehaviour = new IstarParallelBehaviour(this, mainElement, ParallelBehaviour.WHEN_ANY);
						}
						else if (mainElement.getMeansEndLink().getType()==IElementLink.UNIQUE){
							subBehaviour = new MeansEndUniqueBehaviour(this, (IElement)mainElement);
						}
					} catch (ClassCastException ce) {
						System.out.println("Problems in " + mainElement.getName());
						ce.printStackTrace();
						if (mainElement.isBasic()){
							behaviour = ((IBasicElement)mainElement).getBehaviour();
						}
						else {
							if (mainElement.getClass()==Task.class){
								if (((Task)mainElement).getMyDecompositionLink().getType()==IElementLink.SEQUENTIAL){
									behaviour = new SequentialTaskBehaviour(this, (Task)mainElement);
								}
								else if (((Task)mainElement).getMyDecompositionLink().getType()==IElementLink.PARALLEL){
									behaviour = new IstarParallelBehaviour(this, mainElement, ParallelBehaviour.WHEN_ALL);
								}
							} 
							else {
								if (mainElement.getMeansEndLink().getType()==IElementLink.PARALLEL){
									behaviour = new IstarParallelBehaviour(this, mainElement, ParallelBehaviour.WHEN_ANY);
								}
								else if (mainElement.getMeansEndLink().getType()==IElementLink.UNIQUE){
									behaviour = new MeansEndUniqueBehaviour(this, mainElement);
								}
							}
						}
					}
					if (behaviour!=null){

						FSMBehaviour fsm = new FSMBehaviour(this) {
							public int onEnd() {
								myAgent.doDelete();
								return super.onEnd();
							}
						};
						
						fsm.registerTransition(initial_state, initial_state, 0);
						fsm.registerTransition(initial_state, active_state, 1);
						fsm.registerTransition(initial_state, FINAL, 2);
						fsm.registerTransition(active_state, active_state, 0);
						fsm.registerTransition(active_state, reset_state, 1);
						fsm.registerDefaultTransition(reset_state, initial_state);
						//fsm.registerDefaultTransition(reset_state, FINAL);

						fsm.registerTransition(active_state, error_state, AbstractIstarBehaviour.ERROR_STATE_CODE);
						fsm.registerDefaultTransition(error_state, reset_state);
						
						fsm.registerState(new ResetBehaviour(fsm), reset_state);

						fsm.registerFirstState(behaviour, initial_state);
						fsm.registerState(subBehaviour, active_state);
						fsm.registerLastState(new EndBehaviour(), FINAL);

						fsm.registerState(new ErrorBehaviour(fsm), error_state);

						log("behaviour type=" + behaviour.getClass()+ " name=" + behaviour.getBehaviourName()+ " state=" +  behaviour.getExecutionState());
						log("---------------------");
						log("FSM :" + fsm.getChildren().toString() );
						log("---------------------");
						//this.addBehaviour(fsm);
						mainBehaviour.addSubBehaviour(fsm);
					}
					else {
						throw new Exception("behaviour is null!");
					}
				}
				this.addBehaviour(mainBehaviour);
			}
		
		}
		
	}
	private class EndBehaviour extends OneShotBehaviour {
		public void action() {
			log("behaviour name=" + getBehaviourName()+ " state=" +  getExecutionState() + "in EndBehaviour!!!");
		}

		@Override
		public int onEnd() {
			log("behaviour name=" + getBehaviourName()+ " state=" +  getExecutionState() + "in EndBehaviour!!!");
			return 1;
		}
	}

	private class ErrorBehaviour extends OneShotBehaviour {
		
		FSMBehaviour fsm;
		public ErrorBehaviour(FSMBehaviour _fsm){
			super();
			this.fsm = _fsm  ;
		}
		
		@Override
		public void action() {
			IistarBehaviour active = (IistarBehaviour)fsm.getState(ACTIVE);
			ACLMessage workingMsg = ((IstarJadeAgent)myAgent).getWorkingMessages().get(active.getExecutionNumber()); 
			ACLMessage reply = workingMsg.createReply();
			reply.setPerformative(ACLMessage.FAILURE);
			//reply.setContent("Error: " + active.getElement().getContent());
			((IstarJadeAgent)myAgent).send(reply);
			log("Sending failure message to " + workingMsg.getSender().getName() + " in reply to " + reply.getConversationId());
		}
	}
	/*
	private class ResetBehaviour extends OneShotBehaviour {
		private FSMBehaviour mainFSM;
		public ResetBehaviour(FSMBehaviour _fsm){
			super();
			this.mainFSM= _fsm;
		}
		public void action() {
			log(getBehaviourName()+ " state=" +  getExecutionState());
			mainFSM.reset();
			if (myAgent.getAID().getLocalName().equals("operator1")){
				System.out.println("----------------------------------------------------------------");
				System.out.println("********************** Starting new cycle **********************");
				System.out.println("----------------------------------------------------------------");
			}
			log("----------------------------------------------------------------");
			log("********************** Starting new cycle **********************");
			log("----------------------------------------------------------------");

		}

		@Override
		public int onEnd() {
			log("behaviour name=" + getBehaviourName()+ " state=" +  getExecutionState() + "in EndBehaviour!!!");
			return 1;
		}
	}
	*/
	private class ResetBehaviour extends OneShotBehaviour {
		private Behaviour main, fsm;

		public ResetBehaviour(Behaviour _main, FSMBehaviour _fsm){
			super();
			this.main= _main;
			this.fsm = _fsm;
		}
		
		public ResetBehaviour(FSMBehaviour _fsm){
			super();
			this.fsm = _fsm;
		}
		
		public void action() {
			log(getBehaviourName()+ " state=" +  getExecutionState());
			fsm.reset();
			if (myAgent.getAID().getLocalName().equals("operator1")){
				System.out.println("----------------------------------------------------------------");
				System.out.println("********************** Starting new cycle **********************");
				System.out.println("----------------------------------------------------------------");
			}
			log("----------------------------------------------------------------");
			log("********************** Starting new cycle **********************");
			log("----------------------------------------------------------------");

		}

		@Override
		public int onEnd() {
			log("behaviour name=" + getBehaviourName()+ " state=" +  getExecutionState() + "in ResetBehaviour!!!");
			return 1;
		}
	}
	
	protected void searchServices(ServiceDescription serviceDesc){
	  	// Search for services of type "weather-forecast"
		log("service type=" + serviceDesc.getType() + " is being searched ");
	  	try {
	  		// Build the description used as template for the search
	  		DFAgentDescription template = new DFAgentDescription();
	  		template.addServices(serviceDesc);
	  		
	  		
	  		DFAgentDescription[] results = DFService.search(this, template);
  			log("service type=" + serviceDesc.getType() + " found=" + results.length );
	  		if (results.length > 0) {
	  			//System.out.println("Agent "+getLocalName()+" found the following "+ serviceDesc.getType()+ " services:");
	  			
	  			
	  			for (int i = 0; i < results.length; ++i) {
	  				DFAgentDescription dfd = results[i];
	  				AID provider = dfd.getName();
	  				// The same agent may provide several services; we are only interested
	  				// in the weather-forcast one
	  				Iterator it = dfd.getAllServices();
	  				while (it.hasNext()) {
	  					ServiceDescription sd = (ServiceDescription) it.next();
	  					if (sd.getType().equals(serviceDesc.getType())) {
	  			  			log("service type=" + serviceDesc.getType() + " name=" + sd.getName()+" provided by the agent "+provider.getName());
	  					}
	  				}
	  			}
	  		}	
	  		else {
	  			log("Any provider for "+ serviceDesc.getType()+ " was found.");
	  		}
	  	}
	  	catch (FIPAException fe) {
	  		fe.printStackTrace();
	  	}

	}
	
	private void registerServices(List<ServiceDescription> _listSD){
  		DFAgentDescription dfd = new DFAgentDescription();
  		AID aid = getAID();
  		dfd.setName(getAID());
  		for (int i=0;i<_listSD.size();i++){
  			dfd.addServices(_listSD.get(i));
  			log("service type="+ _listSD.get(i).getType()+ " name="+_listSD.get(i).getName()+ " registering");
  		}
  		try {
			// Register the service
  			DFService.register(this, aid, dfd);
	  	}
	  	catch (FIPAException fe) {
	  		fe.printStackTrace();
	  	}
	}
	
	private void addServices(List<ServiceDescription> _listSD){
  		DFAgentDescription dfd = new DFAgentDescription();
  		AID aid = getAID();
  		dfd.setName(aid);
  		for (int i=0;i<_listSD.size();i++){
  	  		dfd.addServices(_listSD.get(i));
  			log("service type="+ _listSD.get(i).getType()+ " name="+_listSD.get(i).getName());
  		}
  		try {
  			DFAgentDescription myDfd = new DFAgentDescription();
  			myDfd.setName(aid);
  			DFAgentDescription[] dfdList = DFService.search(this, dfd);
  			if (dfdList!=null && dfdList.length>0){
  	  			log("service(s) added");
  				DFService.modify(this, dfd);
  			}
  			else {
  				DFService.register(this, dfd);
  	  			log("service(s) registered");
  			}
  		}
	  	catch (Exception e) {
	  		System.out.println(e.getMessage());
	  	}
	}

	private void removeServices(List<ServiceDescription> _listSD){
  		DFAgentDescription dfd = new DFAgentDescription();
  		AID aid = getAID();
  		dfd.setName(aid);
  		for (int i=0;i<_listSD.size();i++){
  			dfd.removeServices(_listSD.get(i));
  		}
		try {
	  		DFService.modify(this, aid, dfd);
	  	}
	  	catch (FIPAException fe) {
	  		fe.printStackTrace();
	  	}
	}
	
	private List<ServiceDescription> actorServiceDescription(IActor _actor){
		//Iterator<IBasicElement> itDependees = _actor.getDependeeElements().iterator();
		
		List<IElement> dependeeList = new Vector<IElement>();
		
		dependeeList.addAll(_actor.getDependeeElements());
		
		Iterator<IRole> itRoles = this.plays.iterator();
		while (itRoles.hasNext()){
			IRole role = itRoles.next();
			dependeeList.addAll(role.getDependeeElements());
		}
		
		Iterator<IPosition> itPosition = this.occupies.iterator();
		while (itPosition.hasNext()) {
			IPosition position = itPosition.next();
			itRoles = position.getCovers().iterator();
			while (itRoles.hasNext()){
				IRole role = itRoles.next();
				dependeeList.addAll(role.getDependeeElements());
			}
		}
		
		int numberOfElements = dependeeList.size();
  		List<ServiceDescription> listSD = new Vector<ServiceDescription>();
  		
  		//while (itDependees.hasNext()){
		for (int i=0;i<numberOfElements;i++){
  			IElement serviceElement = dependeeList.get(i);
  			if (_actor.able(serviceElement)){
  	  			ServiceDescription sd = new ServiceDescription();
  		  		sd.setName(_actor.getName()+":"+serviceElement.getTopic());
  		  		sd.setType(serviceElement.getTopic());
  		  		sd.setOwnership(_actor.getName());
  		  		// Agents that want to use this service need to "know" the weather-forecast-ontology
  		  		sd.addOntologies(IstarOntology.getInstance().getName());
  		  		// Agents that want to use this service need to "speak" the FIPA-SL language
  		  		sd.addLanguages(FIPANames.ContentLanguage.FIPA_SL);
  		  		sd.addProperties(new Property(serviceElement.getClass().getName(), serviceElement.getId()));
  		  		listSD.add(sd);
  			}
		}
  		return listSD;
	}
	
	private void createAndRegisterTopic(IElement elemnt) throws ServiceException{
		// Periodically send messages about topic "JADE"
		String topicName = elemnt.getClass().getName()+":"+elemnt.getName();
		TopicManagementHelper topicHelper = (TopicManagementHelper)getHelper(TopicManagementHelper.SERVICE_NAME);
		final AID topic = topicHelper.createTopic(topicName);
		topicHelper.register(topic);
		this.log("topic name=" + topicName + " registered ");
	}
	
	public String getNickname() {
		return nickname;
	}

	public void setNickname(String value) {
		this.nickname = value;
	}
	
	public List<IPosition> getOccupies() {
		return occupies;
	}

	public List<IRole> getPlays() {
		return plays;
	}
	
	public List<IElement> getMainElements() {
		List<IElement> mainElements = new Vector<IElement>();
		Iterator<IElement> itInternalElmnts = this.inboundaryElements.iterator();
		while (itInternalElmnts.hasNext()){
			IElement internalElmnt = itInternalElmnts.next();
			if (internalElmnt.isMain())
				mainElements.add(internalElmnt);
		}
		return mainElements;
	}
	
	@Override
	public void addRole(IRole _role) {
		
		IRole cloneRole =null;
		try {
			cloneRole = (IRole)((Role)_role).clone();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		this.plays.add(cloneRole);
		if (cloneRole.getDependeeElements().size()>0)
			addServices(actorServiceDescription(cloneRole ));
	}
	
	private void testClone() throws Exception {
		if (this.getInboundaryElements().size()>0){
			testInternalElements(this.getInboundaryElements(), this);
		}
		else if (!this.plays.isEmpty()){
			Iterator<IRole> itRoles =  this.plays.iterator();
			while (itRoles.hasNext()){
				IRole role = itRoles.next();
				testInternalElements(role.getInboundaryElements(), role);
			}
		}
		else if (!this.occupies.isEmpty()){
			Iterator<IPosition> itPositions = this.occupies.iterator();
			while (itPositions.hasNext()){
				IPosition position = itPositions.next();
				Iterator<IRole> itRoles =  position.getCovers().iterator();
				while (itRoles.hasNext()){
					IRole role = itRoles.next();
					testInternalElements(role.getInboundaryElements(), role);
				}
				
			}
		}
		
	}
	
	private void testInternalElements(List<IElement> internalElements, IActor _onwer) throws Exception{
		Iterator<IElement> itElmts = internalElements.iterator();

		while (itElmts.hasNext()){
			IElement internalElmnt = itElmts.next();
			System.out.println("Agent " +((Agent)this).getName() + " has internal element name=" + internalElmnt.getName());
			if (internalElmnt.getOwner()!= _onwer){
				throw new Exception("Agent " +((Agent)this).getName() + " internal element name=" + internalElmnt.getName() + " has a different owner:" + internalElmnt.getOwner().getName());
			}
			if (internalElmnt.getMeansEndLink()!=null){
				Iterator<IElement> itMeans = internalElmnt.getMeansEndLink().getTargetElements().iterator();
				while (itMeans.hasNext()){
					IElement mean = itMeans.next();
					System.out.println("Agent " +((Agent)this).getName() + " internal element type=" + internalElmnt.getClass() + " name=" + internalElmnt.getName() + " has the mean type=" + mean.getClass() + " name=" + mean.getName());
					if (mean.getOwner()!=_onwer){
						throw new Exception("Agent " +((Agent)this).getName() + " mean element type=" + mean.getClass() + " name=" + mean.getName() + " has a different owner :" + mean.getOwner().getName());
					}
					Iterator<MeansEndLink> itMeansFor = ((Task)mean).getMeansFor().iterator();
					while (itMeansFor.hasNext()){
						IElement end = itMeansFor.next().getOwner();
						if (end.getOwner()!=_onwer){
							throw new Exception("Agent " +((Agent)this).getName() + " mean element name=" + mean.getName() + " is mean for a different owner :" + end.getOwner().getName());
						}
					}
				}
			}
			
			if (internalElmnt.getClass().equals(Task.class)){
				if (((Task)internalElmnt).getMyDecompositionLink()!=null){
					Iterator<IElement> itSubTaskElmts = ((Task)internalElmnt).getMyDecompositionLink().getTargetElements().iterator();
					while (itSubTaskElmts.hasNext()){
						IElement subTaskElmt = itSubTaskElmts.next();
						System.out.println("Agent " +((Agent)this).getName() + " internal element type=" + internalElmnt.getClass() + " name=" + internalElmnt.getName() + " has the subelement type=" + subTaskElmt.getClass() + " name=" + subTaskElmt.getName());
						if (subTaskElmt.getOwner()!=null){
							if (subTaskElmt.getOwner()!= _onwer){
								throw new Exception("Agent " +((Agent)this).getName() + " subtask element type=" + subTaskElmt.getClass() + " name="  + subTaskElmt.getName() + " has a different owner :" + subTaskElmt.getOwner().getName());
							}
							Iterator<TaskDecompositionLink> itSupports = subTaskElmt.getSupports().iterator();
							while (itSupports.hasNext()){
								IElement end = itSupports.next().getOwner();
								if (end.getOwner()!=_onwer){
									throw new Exception("Agent " +((Agent)this).getName() + " subtask element name=" + subTaskElmt.getName() + " is support for a different owner :" + end.getOwner().getName());
								}
							}

						}
						
					}
				}
			}
		}
		System.out.println("------------");
		
	}

	@Override
	public void removeRole(IRole role) {
		Iterator<IElement> itInternalElmnts = role.getInboundaryElements().iterator();
		while (itInternalElmnts.hasNext()){
			this.inboundaryElements.remove(itInternalElmnts.next());
		}
		
		this.plays.remove(role);
		if (role.getDependeeElements().size()>0)
			removeServices(actorServiceDescription(role));
	}


	@Override
	public void addPosition(IPosition position) {
		Iterator<IRole> itRoles = position.getCovers().iterator();
		while (itRoles.hasNext()){
			this.addRole(itRoles.next());
		}
		this.occupies.add(position);
		
	}

	@Override
	public void removePosition(IPosition position) {
		Iterator<IRole> itRoles = position.getCovers().iterator();
		while (itRoles.hasNext()){
			this.removeRole(itRoles.next());
		}
		this.occupies.remove(position);
	}


	public boolean able(IElement _elmnt) {
		if (this.getInternalElement(_elmnt.getId())!=null){
			if (_elmnt.isBasic()) {
				return true;
			} 
			else {
				if (_elmnt.getMeansEndLink()!=null)
					return true;
			}
		}

		Iterator<IDependency> itDeps = _elmnt.getDependencies().iterator();
		while (itDeps.hasNext()){
			IDependency dep = itDeps.next();
			if (dep.getDependee().equals(this)){
				if (dep.getDependeeElement()!=null){
					if (this.able(dep.getDependeeElement()))
						return true;
				}
			}
		}

		Iterator<IRole> itRoles = this.getPlays().iterator();
		while (itRoles.hasNext()){
			IRole role = itRoles.next();
			if (_elmnt.able(role))
				return true;
		}
		return false;
	}

	public boolean workable(IElement elmnt) {
		if (elmnt.workable(this))
			return true;
		Iterator<IRole> itRoles = this.getPlays().iterator();
		while (itRoles.hasNext()){
			IRole role = itRoles.next();
			if (elmnt.workable(role))
				return true;
		}
		return false;
	}

	public IElement getInternalElement(String _elmntID){
		Iterator<IElement> itElmnts = inboundaryElements.iterator();
		while (itElmnts.hasNext()){
			IElement _elmnt = itElmnts.next();
			if (_elmnt.getId().equals(_elmntID))
				return _elmnt;
		}
		return null;
	}

	public IElement getInternalElementByName(String _name){
		Iterator<IElement> itElmnts = inboundaryElements.iterator();
		while (itElmnts.hasNext()){
			IElement _elmnt = itElmnts.next();
			if (_elmnt.getName().equals(_name))
				return _elmnt;
		}
		if (!this.plays.isEmpty()){
			IElement _elmnt = null;
			while (this.plays.iterator().hasNext()) {
				IRole role = (IRole) this.plays.iterator().next();
				_elmnt = role.getInternalElementByName(_name);
				if (_elmnt != null)
					return _elmnt;
			}
		}
		return null;
	}
	
	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public List<IElement> getDependeeElements() {
		return this.dependerElements;
		/*
		List<IElement> dependeeList = new Vector<IElement>();
		dependeeList.addAll(this.dependeeElements);
		Iterator<IRole> itRoles = this.plays.iterator();
		while (itRoles.hasNext()){
			IRole role = itRoles.next();
			dependeeList.addAll(role.getDependeeElements());
			
		}
		return dependeeList;
		*/
	}

	@Override
	public List<IElement> getAllDependeeElements() {
		List<IElement> dependeeList = new Vector<IElement>();
		dependeeList.addAll(this.dependeeElements);
		Iterator<IRole> itRoles = this.plays.iterator();
		while (itRoles.hasNext()){
			IRole role = itRoles.next();
			dependeeList.addAll(role.getDependeeElements());
			
		}
		return dependeeList;
	}

	

	public void setDependeeElements(List<IElement> dependeeElements) {
		this.dependeeElements = dependeeElements;
	}

	public List<IElement> getDependerElements() {
		return dependerElements;
	}

	public void setDependerElements(List<IElement> dependerElements) {
		this.dependerElements = dependerElements;
	}

	public List<IElement> getInboundaryElements() {
		return inboundaryElements;
	}

	public void setInboundaryElements(List<IElement> inboundaryElements) {
		this.inboundaryElements = inboundaryElements;
	}

	public void setOccupies(List<IPosition> occupies) {
		this.occupies = occupies;
	}
	
	public void addInternalElement(IElement elmnt) {
		inboundaryElements.add(elmnt);
		elmnt.setOwner(this);
	}

	public List<Belief> getBeliefs() {
		return beliefs;
	}

	public void setBeliefs(List<Belief> beliefs) {
		this.beliefs = beliefs;
	}

	public List<IResource> getResources() {
		return resources;
	}
	

	public void log(String _phrase){
		if (this.log){
			if (logPath==null){
				try {
					
					
					BufferedReader in = new BufferedReader(new FileReader(configurationPath));
					
					String str;
				    while ((str = in.readLine()) != null) {
				        if (str.startsWith("LogFile=")){
				        	logPath = str.substring(8);
				        }
				    }
				    in.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			Log.getInstance(logPath).log(this.getLocalName(), _phrase);
		}
	}

	public AID getSender() {
		return sender;
	}

	public void setSender(AID sender) {
		this.sender = sender;
	}

	public HashMap<String, Object> getShortTermMemory() {
		return shortTermMemory;
	}

	public HashMap<String, ACLMessage> getWorkingMessages() {
		return workingMessages;
	}
	
	public HashMap<String, ACLMessage> getWaitingMessages() {
		return waitingMessages;
	}

	
}

